github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/migration/encryption.go (about)

     1  package migration
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  
     7  	"code.cloudfoundry.org/lager"
     8  	"github.com/pf-qiu/concourse/v6/atc/db/encryption"
     9  )
    10  
    11  var encryptedColumns = []encryptedColumn{
    12  	{"teams", "legacy_auth", "id"},
    13  	{"resources", "config", "id"},
    14  	{"jobs", "config", "id"},
    15  	{"resource_types", "config", "id"},
    16  	{"builds", "private_plan", "id"},
    17  	{"cert_cache", "cert", "domain"},
    18  	{"pipelines", "var_sources", "id"},
    19  }
    20  
    21  type encryptedColumn struct {
    22  	Table      string
    23  	Column     string
    24  	PrimaryKey string
    25  }
    26  
    27  func (self migrator) encryptPlaintext(key *encryption.Key) error {
    28  	logger := self.logger.Session("encrypt")
    29  	for _, ec := range encryptedColumns {
    30  		rows, err := self.db.Query(`
    31  			SELECT ` + ec.PrimaryKey + `, ` + ec.Column + `
    32  			FROM ` + ec.Table + `
    33  			WHERE nonce IS NULL
    34  			AND ` + ec.Column + ` IS NOT NULL
    35  		`)
    36  		if err != nil {
    37  			return err
    38  		}
    39  
    40  		tLog := logger.Session("table", lager.Data{
    41  			"table": ec.Table,
    42  		})
    43  
    44  		encryptedRows := 0
    45  
    46  		for rows.Next() {
    47  			var (
    48  				primaryKey interface{}
    49  				val        sql.NullString
    50  			)
    51  
    52  			err := rows.Scan(&primaryKey, &val)
    53  			if err != nil {
    54  				tLog.Error("failed-to-scan", err)
    55  				return err
    56  			}
    57  
    58  			if !val.Valid {
    59  				continue
    60  			}
    61  
    62  			rLog := tLog.Session("row", lager.Data{
    63  				"primary-key": primaryKey,
    64  			})
    65  
    66  			encrypted, nonce, err := key.Encrypt([]byte(val.String))
    67  			if err != nil {
    68  				rLog.Error("failed-to-encrypt", err)
    69  				return err
    70  			}
    71  
    72  			_, err = self.db.Exec(`
    73  				UPDATE `+ec.Table+`
    74  				SET `+ec.Column+` = $1, nonce = $2
    75  				WHERE `+ec.PrimaryKey+` = $3
    76  			`, encrypted, nonce, primaryKey)
    77  			if err != nil {
    78  				rLog.Error("failed-to-update", err)
    79  				return err
    80  			}
    81  
    82  			encryptedRows++
    83  		}
    84  
    85  		if encryptedRows > 0 {
    86  			tLog.Info("encrypted-existing-plaintext-data", lager.Data{
    87  				"rows": encryptedRows,
    88  			})
    89  		}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  func (self migrator) decryptToPlaintext(oldKey *encryption.Key) error {
    96  	logger := self.logger.Session("decrypt")
    97  	for _, ec := range encryptedColumns {
    98  		rows, err := self.db.Query(`
    99  			SELECT ` + ec.PrimaryKey + `, nonce, ` + ec.Column + `
   100  			FROM ` + ec.Table + `
   101  			WHERE nonce IS NOT NULL
   102  		`)
   103  		if err != nil {
   104  			return err
   105  		}
   106  
   107  		tLog := logger.Session("table", lager.Data{
   108  			"table": ec.Table,
   109  		})
   110  
   111  		decryptedRows := 0
   112  
   113  		for rows.Next() {
   114  			var (
   115  				primaryKey interface{}
   116  				val, nonce string
   117  			)
   118  
   119  			err := rows.Scan(&primaryKey, &nonce, &val)
   120  			if err != nil {
   121  				tLog.Error("failed-to-scan", err)
   122  				return err
   123  			}
   124  
   125  			rLog := tLog.Session("row", lager.Data{
   126  				"primary-key": primaryKey,
   127  			})
   128  
   129  			decrypted, err := oldKey.Decrypt(val, &nonce)
   130  			if err != nil {
   131  				rLog.Error("failed-to-decrypt", err)
   132  				return err
   133  			}
   134  
   135  			_, err = self.db.Exec(`
   136  				UPDATE `+ec.Table+`
   137  				SET `+ec.Column+` = $1, nonce = NULL
   138  				WHERE `+ec.PrimaryKey+` = $2
   139  			`, decrypted, primaryKey)
   140  			if err != nil {
   141  				rLog.Error("failed-to-update", err)
   142  				return err
   143  			}
   144  
   145  			decryptedRows++
   146  		}
   147  
   148  		if decryptedRows > 0 {
   149  			tLog.Info("decrypted-existing-encrypted-data", lager.Data{
   150  				"rows": decryptedRows,
   151  			})
   152  		}
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  var ErrEncryptedWithUnknownKey = errors.New("row encrypted with neither old nor new key")
   159  
   160  func (self migrator) encryptWithNewKey(newKey *encryption.Key, oldKey *encryption.Key) error {
   161  	logger := self.logger.Session("rotate")
   162  	for _, ec := range encryptedColumns {
   163  		rows, err := self.db.Query(`
   164  			SELECT ` + ec.PrimaryKey + `, nonce, ` + ec.Column + `
   165  			FROM ` + ec.Table + `
   166  			WHERE nonce IS NOT NULL
   167  		`)
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		tLog := logger.Session("table", lager.Data{
   173  			"table": ec.Table,
   174  		})
   175  
   176  		encryptedRows := 0
   177  
   178  		for rows.Next() {
   179  			var (
   180  				primaryKey interface{}
   181  				val, nonce string
   182  			)
   183  
   184  			err := rows.Scan(&primaryKey, &nonce, &val)
   185  			if err != nil {
   186  				tLog.Error("failed-to-scan", err)
   187  				return err
   188  			}
   189  
   190  			rLog := tLog.Session("row", lager.Data{
   191  				"primary-key": primaryKey,
   192  			})
   193  
   194  			decrypted, err := oldKey.Decrypt(val, &nonce)
   195  			if err != nil {
   196  				_, err = newKey.Decrypt(val, &nonce)
   197  				if err == nil {
   198  					rLog.Debug("already-encrypted-with-new-key")
   199  					continue
   200  				}
   201  
   202  				logger.Error("failed-to-decrypt-with-either-key", err)
   203  				return ErrEncryptedWithUnknownKey
   204  			}
   205  
   206  			encrypted, newNonce, err := newKey.Encrypt(decrypted)
   207  			if err != nil {
   208  				rLog.Error("failed-to-encrypt", err)
   209  				return err
   210  			}
   211  
   212  			_, err = self.db.Exec(`
   213  				UPDATE `+ec.Table+`
   214  				SET `+ec.Column+` = $1, nonce = $2
   215  				WHERE `+ec.PrimaryKey+` = $3
   216  			`, encrypted, newNonce, primaryKey)
   217  			if err != nil {
   218  				rLog.Error("failed-to-update", err)
   219  				return err
   220  			}
   221  
   222  			encryptedRows++
   223  		}
   224  
   225  		if encryptedRows > 0 {
   226  			tLog.Info("re-encrypted-existing-encrypted-data", lager.Data{
   227  				"rows": encryptedRows,
   228  			})
   229  		}
   230  	}
   231  
   232  	return nil
   233  }